test(plugins): serve batch 2's four network-escape probes from doubles - #8013
Merged
Conversation
The three plugin-kanban files and the one plugin-gantt file left in the network-escape ledger for `/api/v1/security/explain` now answer that probe from a recording double instead of a real socket, and their lines leave `KNOWN_ESCAPES` and `PINNED_LEDGER` together — 16 rows to 12 in both. Mechanism, traced with a stack probe on the guard's attribution point rather than assumed: all four go through ONE hook. RecordDetailDrawer renders DetailView, which calls `useRecordEditable` twice per open (edit, then delete); at `useRecordEditable.ts:76` the hook degrades from the host's authenticated `apiFetch` to the global `fetch`, and under happy-dom that global is a real HTTP client whose document URL is `http://localhost:3000`. The read is best-effort, which is why every one of these files stayed green while its request always failed. The double is the shape #5225 settled on and batch 1 landed in `ObjectCalendar.navWidthDefault.test.tsx`: a router, not a sink. It records every URL it is handed and `afterEach` fails on any URL that is not the explain route, so an escape elsewhere reds here instead of vanishing into the hook's `catch`; `cleanup()` runs before `vi.unstubAllGlobals()` so no verdict effect can settle with the real fetch back in place (#7439). It answers the permissive verdict, which is the same downstream state the failing request produced — `useRecordEditable` initialises `allowed` to true and its failure path leaves it there — so no existing assertion changes meaning. Nothing is skipped, quarantined or silenced. Part of #7307 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MM7kaS4dPpYHV5BsMyu4tQ
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MM7kaS4dPpYHV5BsMyu4tQ
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contributor
Author
|
Standing-down note — Generated by Claude Code |
baozhoutao
marked this pull request as ready for review
September 6, 2026 09:20
This was referenced Sep 6, 2026
baozhoutao
pushed a commit
that referenced
this pull request
Sep 6, 2026
Batch 18 (PR #8009) and objectui#7307 batch 2 (PR #8013) landed while this batch was measuring. Both gate-file changes are removals inside UNGATED_DOCS on different lines, for different documents. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
This was referenced Sep 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #7307 — batch 2 of the network-escape burn-down (batch 1 was #7999).
The four
/api/v1/security/explainrows left in the ledger forplugin-kanbanandplugin-ganttnow serve that probe from the recording double batch 1 landed, andtheir lines leave
KNOWN_ESCAPESandPINNED_LEDGERin the same commit.Per file
packages/plugin-kanban/src/ObjectKanban.navWidthDefault.test.tsxPOST /api/v1/security/explainuseRecordEditable.ts:76(apiFetch ?? fetch)installExplainDouble()inbeforeEachpackages/plugin-kanban/src/ObjectKanban.overlayTitleI18n.test.tsxPOST /api/v1/security/explainuseRecordEditable.ts:76installExplainDouble()inbeforeEachpackages/plugin-kanban/src/ObjectKanban.overlayTitleNoProviderFallback.test.tsxPOST /api/v1/security/explainuseRecordEditable.ts:76installExplainDouble()inbeforeEachpackages/plugin-gantt/src/ObjectGantt.navWidthDefault.test.tsxPOST /api/v1/security/explainuseRecordEditable.ts:76installExplainDouble()inbeforeEachMechanism, measured rather than assumed. A trap-guarded stack probe on the
guard's own attribution point says all four take ONE route, not the two batch 1
found:
RecordDetailDrawerrendersDetailView, which callsuseRecordEditabletwice per open (
DetailView.tsx:290for edit,:296for delete). AtuseRecordEditable.ts:76the hook degrades from the host's authenticatedapiFetchto the globalfetch— by design, so a standalone embed keepsrendering — and under happy-dom that global is a real HTTP client whose document
URL is
http://localhost:3000, so the relative path resolved to a live socket.The read is best-effort (
catchleaves the record editable), which is why everyone of these files stayed green while its request always failed. The probe was
reverted by
git checkout HEAD --and the guard is byte-identical toHEADafterwards.
The double is batch 1's shape verbatim:
vi.stubGlobal('fetch', router)inbeforeEach,cleanup()beforevi.unstubAllGlobals()inafterEach(#7439ordering). It is a router, not a sink — it records every URL it is handed and
afterEachfails on any URL that is not the explain route, so an escapeelsewhere reds here instead of vanishing into the hook's
catch. It answers thepermissive verdict, which is the same downstream state the failing request
produced (
useRecordEditableinitialisesallowedtotrueand its failurepath leaves it there), so no existing assertion changes meaning. Only the
single-
recordIdresponse shape is reached from these four files; the batchedbranch is kept so the router stays byte-identical to its siblings rather than
forking per file. Nothing is skipped, quarantined or silenced.
Ledger arithmetic
16 to 12, in both lists. The four names are deleted from
KNOWN_ESCAPESinvitest.setup.network-escape-guard.tsand fromPINNED_LEDGERinscripts/__tests__/network-escape-ledger.test.tsin this one commit. Checked inlockstep by diffing the quoted paths of the two literals against each other on
the branch: empty diff, both at 12. Remaining 12: app-shell 8, plugin-detail 4.
Evidence
Per file, before then after (attribution lines /
ECONNREFUSEDlines, then thepost-fix run):
ObjectKanban.navWidthDefaultTests 3 passedObjectKanban.overlayTitleI18nTests 7 passedObjectKanban.overlayTitleNoProviderFallbackTests 2 passedObjectGantt.navWidthDefaultTests 2 passedpnpm exec vitest run packages/plugin-kanban/ packages/plugin-gantt/ scripts/__tests__/network-escape-ledger.test.tson this head: exit 0,
Test Files 87 passed (87),Tests 619 passed (619), andzero lines matching
network-escapeorECONNREFUSEDin the whole run.Ablation (on the committed tree, one script with
trap ... EXIT INT TERMandabsolute paths). One converted file's double reverted to its pre-batch-2 blob
while its line stayed deleted from BOTH ledgers. Mutation proven on disk:
ObjectGantt.navWidthDefault.test.tsxblobe9ba5b8eto28f37a60(equal tothe
e1545cfbase blob), anchorsinstallExplainDouble2 to 0 andvi.stubGlobal1 to 0, that path present 0 times in each ledger during the run.Predicted direction red; OBSERVED red — exit 1,
Tests 2 failed (2),Network escape: this test reached a REAL socket at http://localhost:3000/api/v1/security/explainnaming
file: packages/plugin-gantt/src/ObjectGantt.navWidthDefault.test.tsx.Restore proven:
git checkout HEAD -- path, blob back toe9ba5b8e,git diff HEADon that path empty,git status --porcelainempty.Gates, exit codes captured by redirect-then-capture, never through a pipe:
node scripts/check-changeset-presence.mjsexit 0 — "4 source file(s) of 2released package(s) changed, and this change declares 1 changeset(s)"; the
changeset has an EMPTY frontmatter, the explicit exemption for a test-only
change under a released package.
pnpm check:control-bytesexit 0 (6443 tracked text files), plus agrep -naPself-scan of the control range over all 7 changed paths, no hits.node scripts/check-governed-queue-guard.mjs --testover all 7 changed paths:exit 0, "NOT GOVERNED — 7 path(s) checked against 5 governed surface(s)".
pnpm type-check:scriptsexit 0 ·pnpm check:vi-mock-inheritexit 0 ·pnpm check:vi-mock-specifiersexit 0.type-checkandlintforplugin-kanbanandplugin-gantt:exit 0, 0 errors, proven non-vacuous rather than assumed —
tsc -p tsconfig.test.json --listFilesnames each edited test file exactlyonce, and
eslint --format jsonlists each edited file among the linted set(37 and 91 files). Warning counts unchanged: every
no-explicit-anywarning inthe touched files sits on a pre-existing line above this diff's hunks. The
type-checktaskdependsOn ^build, so turbo built the dependency closure.Concurrency note (A3)
Yes — a #5174 batch-18 PR on one of these packages exists: #8009,
docs(plugin-gantt): compile the gantt README's 18 blocks and drop its ledger entry, opened after this branch was cut. Its file list read over git ispackages/plugin-gantt/README.mdandscripts/check-doc-snippet-types.mjs—zero overlap with the 7 files here.
mainmoved by two commits while this batchwas in flight (#8003, #8002) and has been merged into this branch; the suites,
the ablation and every gate above were re-run on the merged head after that.
Live E2E (informational)is red on every branch today for an upstream reason(#7990 / objectstack#16186), not for anything in this diff.
🤖 Generated with Claude Code
https://claude.ai/code/session_01MM7kaS4dPpYHV5BsMyu4tQ
Generated by Claude Code